package timing_wheel

import (
	"amusing-pkg/maputils"
	"container/list"
	"sync"
	"sync/atomic"
	"time"
)

type TimeWheel struct {
	sync.Once
	taskMap         *maputils.Map[uint64, *list.Element] // 任务Map
	slots           []*list.List                         // 任务槽
	tickMs          time.Duration                        // 基本时间跨度
	ticker          *time.Ticker                         // 定时器
	current         atomic.Int32                         // 现在指向的槽位
	wheelSize       int32                                // 槽数
	overallInterval time.Duration                        // 总体时间跨度
	openLevel       bool                                 // 开启层级
	lowestLevel     bool                                 // 是否为最底层级
	addTaskCh       chan *Task                           // 添加任务Task
	downgradeTaskCh chan *Task                           // 降级接口 用于跨层次运输Task 只会向低级处运输
	removeChannel   chan uint64                          // 删除接口
	stopChannel     chan struct{}                        // 结束接口
}

/*
NewTimeWheelWithRun new timeWheel

	tickMs 基本时间跨度
	wheelSize 时间槽槽数,
	openLevel 是否开启层级时间轮
	lowestLevel 是否开最底层时间轮
	downgradeTaskCh 任务转移时间轮通道
*/
func NewTimeWheelWithRun(tickMs time.Duration, wheelSize int32, openLevel, lowestLevel bool, downgradeTaskCh chan *Task) *TimeWheel {
	timeWheel := TimeWheel{
		tickMs:          tickMs,
		ticker:          time.NewTicker(tickMs),
		wheelSize:       wheelSize,
		lowestLevel:     lowestLevel,
		overallInterval: tickMs * time.Duration(wheelSize),
		openLevel:       openLevel,
		current:         atomic.Int32{},
	}
	timeWheel.Once.Do(func() {
		timeWheel.taskMap = maputils.New[uint64, *list.Element]()
		timeWheel.slots = make([]*list.List, wheelSize)
		// init timeWheel slots
		for i := range timeWheel.slots {
			timeWheel.slots[i] = list.New()
		}
		if openLevel && !lowestLevel {
			timeWheel.downgradeTaskCh = downgradeTaskCh
		}
		timeWheel.removeChannel = make(chan uint64, 1024)
		timeWheel.stopChannel = make(chan struct{}, 8)
		timeWheel.addTaskCh = make(chan *Task, 1024)
	})

	go timeWheel.run()
	return &timeWheel
}

func (t *TimeWheel) runWithLowestLevel() {
	for {
		select {
		// current next and run job
		case <-t.ticker.C:
			current := t.current.Load()
			if current+1 < t.wheelSize {
				t.current.Add(1)
			} else {
				// reset timeWheel current
				t.current.Store(0)
			}
			t.processTasks(current)

		//	add task
		case task := <-t.addTaskCh:
			t.addTask(task)
		// remove task
		case taskID := <-t.removeChannel:
			t.removeTask(taskID)
		case <-t.stopChannel:
			t.stop()
			return
		}
	}
}

func (t *TimeWheel) addTask(task *Task) {
	// 临界值问题，直接执行
	//if task.timeLeft == 0 {
	//
	//}
	index := t.current.Load()

	var moveIndex int32
	if task.timeLeft > t.overallInterval {
		task.circle = int(task.timeLeft / t.overallInterval)
		task.timeLeft = task.timeLeft % t.overallInterval
	}

	if task.timeLeft > 0 {
		moveIndex = int32(task.timeLeft/t.tickMs) - 1
		//  临界值处理: 如果恰好等于当前轮的基本跨度且不为最低级时间轮
		if moveIndex == 0 {
			if !t.lowestLevel {
				t.downgradeTaskCh <- task
				return
			}
			// need run task job
			task.job()

			return
		}
		task.timeLeft = task.timeLeft % t.tickMs
	}

	task.slotsIndex = (index + moveIndex) % t.wheelSize

	t.taskMap.Store(task.id,
		t.slots[task.slotsIndex].PushBack(task),
	)
	return
}

func (t *TimeWheel) removeTask(taskID uint64) {
	element, ok := t.taskMap.Load(taskID)
	if !ok {
		return
	}
	task := element.Value.(*Task)
	t.slots[task.slotsIndex].Remove(element)
	t.taskMap.Delete(taskID)
	element = nil

}

func (t *TimeWheel) run() {
	t.runWithLowestLevel()
}

func (t *TimeWheel) processTasks(current int32) {
	// 遍历当前槽位的任务列表，异步执行任务
	for e := t.slots[current].Front(); e != nil; e = e.Next() {
		task, ok := e.Value.(*Task)
		if !ok {
			continue
		}
		// 异步执行任务
		go func(task *Task) {
			// not has circle can run or move to the next timeWheel
			if task.circle < 1 {
				if t.openLevel && !t.lowestLevel {
					t.downgradeTaskCh <- task
					//	need remove this task form taskMap and list
					t.remove(task.id)
					return
				}
				task.job()
				t.remove(task.id)
			}
			task.circle--
		}(task)
	}
}

func (t *TimeWheel) addJob(id uint64, after time.Duration, job TimeWheelJob) {
	t.addTaskCh <- &Task{
		timeLeft: after,
		id:       id,
		job:      job,
	}
}

func (t *TimeWheel) remove(id uint64) {
	t.removeChannel <- id
}

func (t *TimeWheel) stop() {
	close(t.addTaskCh)
	close(t.removeChannel)
	close(t.stopChannel)
}

func (t *TimeWheel) AddJob(id uint64, after time.Duration, job TimeWheelJob) {
	t.addTaskCh <- &Task{
		timeLeft: after,
		id:       id,
		job:      job,
	}
}

func (t *TimeWheel) GetAddCh() chan *Task {
	return t.addTaskCh
}

func (t *TimeWheel) Remove(id uint64) {
	t.remove(id)
}

func (t *TimeWheel) Stop() {
	t.stop()
}

type TimeWheelJob func()
type Task struct {
	slotsIndex int32
	circle     int
	timeLeft   time.Duration
	id         uint64
	job        TimeWheelJob
}
